package di.scanner.analyzer;

import di.scanner.handler.StatementHandler;
import di.scanner.helper.XMLHelper;
import di.scanner.model.JavaClassElement;
import di.scanner.mongo.dao.MethodInvocationResultDao;
import di.scanner.mongo.dao.impl.MethodInvocationResultDaoImpl;
import di.scanner.mongo.model.MethodInvocationResult;
import di.scanner.store.JavaClassStore;
import org.eclipse.jdt.core.dom.*;

import java.util.*;

public class ClassDenpencyAnalyzer implements Analyzer {

    private JavaClassElement element;
    private MethodInvocationResultDao methodInvocationResultDao;
    private StatementHandler statementHandler;

    public ClassDenpencyAnalyzer() {
        methodInvocationResultDao = new MethodInvocationResultDaoImpl();
        statementHandler = new StatementHandler();
    }

    public void setElement(JavaClassElement element) {

        this.element = element;
    }

    @Override
    public void analysis() {
        String domainName = "";
        while((element = JavaClassStore.getInstance().poll())!=null){
            domainName = element.getDomainName();
            analysisme();

        }
        System.out.println(domainName+" analysis done");
    }

    public void analysisme() {
        List<MethodDeclaration> methodDeclarationList = element.getMethodDeclarationList();
        PackageDeclaration packageDeclaration = element.getPackageDeclaration();
        element.buildFieldMap();
        element.buildImportMap();
        methodDeclarationList.forEach((methodDeclaration -> {
            analysisMethod(packageDeclaration, methodDeclaration);
        }));
    }

    private void analysisMethod(PackageDeclaration packageDeclaration, MethodDeclaration methodDeclaration) {
        Map<String, Object> variableMap = new HashMap<>();
        Map<String, List<String>> methodInvocationMap = new HashMap<>();
        try {
            Block block = methodDeclaration.getBody();
           if (element.getClassName().equals("ShipmentHelper")&&methodDeclaration.getName().getIdentifier().equals("checkIsAMRequired")){
               System.out.println("hahahhahhaha");
           }
            getMethodAndVariableStatement(variableMap, methodInvocationMap, block);
            Set<Map.Entry<String, List<String>>> methodInvocationSet = methodInvocationMap.entrySet();
            Iterator<Map.Entry<String, List<String>>> iterator = methodInvocationSet.iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, List<String>> methodInvocation = iterator.next();
                String variableName = methodInvocation.getKey();
                List<String> methodNameList = methodInvocation.getValue();
                try {
                    Object className = "";
                    MethodInvocationResult methodInvocationResult = new MethodInvocationResult();
                    boolean isRestful = false;
                    if ((!"".equals(variableName))&&variableName.substring(0, 1).toCharArray()[0]>='A'&&variableName.substring(0, 1).toCharArray()[0]<='Z'){
                        className = variableName;
                    }else{
                        className = element.findClassNameByVariable(variableName, variableMap);
                        if (className instanceof ParameterizedType){
                            ParameterizedType parameterizedType = (ParameterizedType) className;
                            Type argumentType = (Type) parameterizedType.typeArguments().get(0);
                            if (argumentType instanceof SimpleType){
                                SimpleType simpleType = (SimpleType) argumentType;
                                className = simpleType.getName().getFullyQualifiedName();
                                isRestful = true;
                            }
                        }
                    }
                    String fullName;
                    if (!JavaClassElement.UNKNOW.equals(className)) {
                        if (className.toString().contains(".")){
                            fullName = className.toString();
                        }else{
                            fullName = element.findPackageNameByClass(className.toString());
                        }
                    } else {
                        fullName = variableName + ": " + JavaClassElement.UNKNOW;
                    }
                    if (isRestful){
                        methodInvocationResult.setRestDto(fullName);
                    }
                    methodInvocationResult.setConsumerClassName(element.getClassName());
                    methodInvocationResult.setConsumerMethodName(methodDeclaration.getName().getIdentifier());
                    methodInvocationResult.setConsumerPackageName(packageDeclaration.getName().getFullyQualifiedName());
                    methodInvocationResult.setProviderClassName(fullName);
                    methodInvocationResult.setProviderMethodNameList(methodNameList);
                    if (element.getModuleName()!=null){
                        methodInvocationResult.setModuleName(element.getModuleName().toUpperCase());
                    }
                    if (element.getDomainName()!=null){
                        methodInvocationResult.setDomainName(element.getDomainName().toUpperCase());
                    }
                    if (XMLHelper.getSoapClassNameList().contains(methodInvocationResult.getProviderClassName())){
                        methodInvocationResult.setServiceType(MethodInvocationResult.WS_SOAP);
                        System.out.println(methodInvocationResult.toString());
                        methodInvocationResultDao.save(methodInvocationResult);
                    }else if (XMLHelper.getRestDtoClassNameList().contains(methodInvocationResult.getRestDto())){
                        methodInvocationResult.setServiceType(MethodInvocationResult.WS_RESTful);
                        methodInvocationResult.setUrl(XMLHelper.getRestfulClassNameMap().get(methodInvocationResult.getProviderClassName()));
                        System.out.println(methodInvocationResult.toString());
                        methodInvocationResultDao.save(methodInvocationResult);

                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            System.out.println(packageDeclaration.getName().getFullyQualifiedName() + "." + element.getClassName() + ":" + methodDeclaration.getName().getIdentifier());
            e.printStackTrace();
        }
    }

    private void getMethodAndVariableStatement(Map<String, Object> variableMap, Map<String, List<String>> methodInvocationMap, Statement statementParam) {
        List<Statement> allStatement = new ArrayList<>();
        statementHandler.handler(statementParam, allStatement);
        allStatement.forEach((statement -> {
            if (statement instanceof VariableDeclarationStatement) {
                analysisVariableDeclaration(variableMap, methodInvocationMap, (VariableDeclarationStatement) statement);
            }
            if (statement instanceof ExpressionStatement) {
                ExpressionStatement expressionStatement = (ExpressionStatement) statement;
                Expression expression = expressionStatement.getExpression();
                if (expression instanceof Assignment){
                    Assignment assignment = (Assignment) expression;
                    expression = assignment.getRightHandSide();
                }
                getMethodInvocation(methodInvocationMap, expression);
            }
            if (statement instanceof ReturnStatement) {
                ReturnStatement returnStatement = (ReturnStatement) statement;
                Expression expression = returnStatement.getExpression();
                getMethodInvocation(methodInvocationMap, expression);
            }
        }));
    }

    private void analysisVariableDeclaration(Map<String, Object> variableMap, Map<String, List<String>> methodInvocationMap, VariableDeclarationStatement statement) {
        VariableDeclarationStatement variableDeclarationStatement = statement;
        Type type = variableDeclarationStatement.getType();
        Object variableClassName = variableDeclarationStatement.getType().toString();
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            variableClassName = parameterizedType.getType().toString();
            if (variableClassName.toString().contains("FWRESTServiceProxy")){
//                Type argumentType = (Type) parameterizedType.typeArguments().get(0);
//                if (argumentType instanceof SimpleType){
//                    SimpleType simpleType = (SimpleType) argumentType;
//                    variableClassName = simpleType.getName().getFullyQualifiedName();
//                }
                variableClassName = parameterizedType;
            }
        }
        VariableDeclarationFragment fragment = (VariableDeclarationFragment) variableDeclarationStatement.fragments().get(0);
        Expression expression = fragment.getInitializer();
        if (expression != null) {
            getMethodInvocation(methodInvocationMap, expression);
        }
        variableMap.put(((VariableDeclarationFragment) variableDeclarationStatement.fragments().get(0)).getName().getIdentifier(), variableClassName);
    }

    private void getMethodInvocation(Map<String, List<String>> methodInvocationMap, Expression expression) {
        if (expression instanceof MethodInvocation) {
            MethodInvocation methodInvocation = (MethodInvocation) expression;
            String methodInvocationVariable = "";
            Expression methodExpress = methodInvocation.getExpression();
            if (methodExpress instanceof SimpleName) {
                SimpleName simpleName = (SimpleName) methodExpress;
                methodInvocationVariable = simpleName.getIdentifier();
            }
            if (methodExpress instanceof FieldAccess) {
                FieldAccess fieldAccess = (FieldAccess) methodExpress;
                methodInvocationVariable = fieldAccess.getName().getIdentifier();
            }
            if (!methodInvocationMap.containsKey(methodInvocationVariable)) {
                methodInvocationMap.put(methodInvocationVariable, new ArrayList<>());
            }
            if (methodExpress instanceof MethodInvocation){
                getMethodInvocation(methodInvocationMap,methodExpress);
            }
            methodInvocationMap.get(methodInvocationVariable).add(methodInvocation.getName().getIdentifier());
        }
    }


}
